<?php
/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category   Zend
 * @package    Zend_Form
 * @subpackage Decorator
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */

/**
 * @see Zend_Form_Decorator_Abstract
 */
require_once 'Zend/Form/Decorator/Abstract.php';

/**
 * Zend_Form_Decorator_Element_HtmlTag
 *
 * Wraps content in an HTML block tag.
 *
 * Options accepted are:
 * - tag: tag to use in decorator
 * - noAttribs: do not render attributes in the opening tag
 * - placement: 'append' or 'prepend'. If 'append', renders opening and
 * closing tag after content; if prepend, renders opening and closing tag
 * before content.
 * - openOnly: render opening tag only
 * - closeOnly: render closing tag only
 *
 * Any other options passed are processed as HTML attributes of the tag.
 *
 * @category   Zend
 * @package    Zend_Form
 * @subpackage Decorator
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: HtmlTag.php 24428 2011-09-02 14:10:03Z matthew $
 */
class Zend_Form_Decorator_HtmlTag extends Zend_Form_Decorator_Abstract {
	/**
	 * Character encoding to use when escaping attributes
	 * @var string
	 */
	protected $_encoding;
	
	/**
	 * Placement; default to surround content
	 * @var string
	 */
	protected $_placement = null;
	
	/**
	 * HTML tag to use
	 * @var string
	 */
	protected $_tag;
	
	/**
	 * @var Zend_Filter
	 */
	protected $_tagFilter;
	
	/**
	 * Convert options to tag attributes
	 *
	 * @return string
	 */
	protected function _htmlAttribs(array $attribs) {
		$xhtml = '';
		$enc = $this->_getEncoding ();
		foreach ( ( array ) $attribs as $key => $val ) {
			$key = htmlspecialchars ( $key, ENT_COMPAT, $enc );
			if (is_array ( $val )) {
				if (array_key_exists ( 'callback', $val ) && is_callable ( $val ['callback'] )) {
					$val = call_user_func ( $val ['callback'], $this );
				} else {
					$val = implode ( ' ', $val );
				}
			}
			$val = htmlspecialchars ( $val, ENT_COMPAT, $enc );
			$xhtml .= " $key=\"$val\"";
		}
		return $xhtml;
	}
	
	/**
	 * Normalize tag
	 *
	 * Ensures tag is alphanumeric characters only, and all lowercase.
	 *
	 * @param  string $tag
	 * @return string
	 */
	public function normalizeTag($tag) {
		if (! isset ( $this->_tagFilter )) {
			require_once 'Zend/Filter.php';
			require_once 'Zend/Filter/Alnum.php';
			require_once 'Zend/Filter/StringToLower.php';
			$this->_tagFilter = new Zend_Filter ();
			$this->_tagFilter->addFilter ( new Zend_Filter_Alnum () )->addFilter ( new Zend_Filter_StringToLower () );
		}
		return $this->_tagFilter->filter ( $tag );
	}
	
	/**
	 * Set tag to use
	 *
	 * @param  string $tag
	 * @return Zend_Form_Decorator_HtmlTag
	 */
	public function setTag($tag) {
		$this->_tag = $this->normalizeTag ( $tag );
		return $this;
	}
	
	/**
	 * Get tag
	 *
	 * If no tag is registered, either via setTag() or as an option, uses 'div'.
	 *
	 * @return string
	 */
	public function getTag() {
		if (null === $this->_tag) {
			if (null === ($tag = $this->getOption ( 'tag' ))) {
				$this->setTag ( 'div' );
			} else {
				$this->setTag ( $tag );
				$this->removeOption ( 'tag' );
			}
		}
		
		return $this->_tag;
	}
	
	/**
	 * Get the formatted open tag
	 *
	 * @param  string $tag
	 * @param  array $attribs
	 * @return string
	 */
	protected function _getOpenTag($tag, array $attribs = null) {
		$html = '<' . $tag;
		if (null !== $attribs) {
			$html .= $this->_htmlAttribs ( $attribs );
		}
		$html .= '>';
		return $html;
	}
	
	/**
	 * Get formatted closing tag
	 *
	 * @param  string $tag
	 * @return string
	 */
	protected function _getCloseTag($tag) {
		return '</' . $tag . '>';
	}
	
	/**
	 * Render content wrapped in an HTML tag
	 *
	 * @param  string $content
	 * @return string
	 */
	public function render($content) {
		$tag = $this->getTag ();
		$placement = $this->getPlacement ();
		$noAttribs = $this->getOption ( 'noAttribs' );
		$openOnly = $this->getOption ( 'openOnly' );
		$closeOnly = $this->getOption ( 'closeOnly' );
		$this->removeOption ( 'noAttribs' );
		$this->removeOption ( 'openOnly' );
		$this->removeOption ( 'closeOnly' );
		
		$attribs = null;
		if (! $noAttribs) {
			$attribs = $this->getOptions ();
		}
		
		switch ($placement) {
			case self::APPEND :
				if ($closeOnly) {
					return $content . $this->_getCloseTag ( $tag );
				}
				if ($openOnly) {
					return $content . $this->_getOpenTag ( $tag, $attribs );
				}
				return $content . $this->_getOpenTag ( $tag, $attribs ) . $this->_getCloseTag ( $tag );
			case self::PREPEND :
				if ($closeOnly) {
					return $this->_getCloseTag ( $tag ) . $content;
				}
				if ($openOnly) {
					return $this->_getOpenTag ( $tag, $attribs ) . $content;
				}
				return $this->_getOpenTag ( $tag, $attribs ) . $this->_getCloseTag ( $tag ) . $content;
			default :
				return (($openOnly || ! $closeOnly) ? $this->_getOpenTag ( $tag, $attribs ) : '') . $content . (($closeOnly || ! $openOnly) ? $this->_getCloseTag ( $tag ) : '');
		}
	}
	
	/**
	 * Get encoding for use with htmlspecialchars()
	 *
	 * @return string
	 */
	protected function _getEncoding() {
		if (null !== $this->_encoding) {
			return $this->_encoding;
		}
		
		if (null === ($element = $this->getElement ())) {
			$this->_encoding = 'UTF-8';
		} elseif (null === ($view = $element->getView ())) {
			$this->_encoding = 'UTF-8';
		} elseif (! $view instanceof Zend_View_Abstract && ! method_exists ( $view, 'getEncoding' )) {
			$this->_encoding = 'UTF-8';
		} else {
			$this->_encoding = $view->getEncoding ();
		}
		return $this->_encoding;
	}
}
